<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <title>事务  &middot; sitename</title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">


<meta name="description" content="事务 description" />

<meta name="keywords" content="one, two, ">


<meta property="og:title" content="事务  &middot; sitename ">
<meta property="og:site_name" content="sitename"/>
<meta property="og:url" content="https://lvzongcheng.gitee.io/redis/%E4%BA%8B%E5%8A%A1/" />
<meta property="og:locale" content="en-EN">


<meta property="og:type" content="article" />
<meta property="og:description" content="事务 description"/>
<meta property="og:article:published_time" content="2017-05-13T00:00:00Z" />
<meta property="og:article:modified_time" content="2017-05-13T00:00:00Z" />

  
    
<meta property="og:article:tag" content="one">
    
<meta property="og:article:tag" content="two">
    
  

  

<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "Article",
    "headline": "事务",
    "author": {
      "@type": "Person",
      "name": "LvZongCheng"
    },
    "datePublished": "2017-05-13",
    "description": "事务 description",
    "wordCount":  226 
  }
</script>



<link rel="canonical" href="https://lvzongcheng.gitee.io/redis/%E4%BA%8B%E5%8A%A1/" />

<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://lvzongcheng.gitee.io/touch-icon-144-precomposed.png">
<link href="https://lvzongcheng.gitee.io/favicon.png" rel="icon">

<meta name="generator" content="Hugo 0.60.0" />

  <!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->

<link href='https://fonts.googleapis.com/css?family=Merriweather:300%7CRaleway%7COpen+Sans' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/highlight/default.css">

  
  
	<script>
	  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
	  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
	  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
	  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

	  ga('create', 'Your Google Analytics tracking code', 'auto');
	  ga('send', 'pageview');

	</script>

</head>
<body>
  <main id="main-wrapper" class="container main_wrapper has-sidebar">
    <header id="main-header" class="container main_header">
  <div class="container brand">
  <div class="container title h1-like">
  <a class="baselink" href="https://lvzongcheng.gitee.io">
  LvZongCheng

</a>

</div>

  
<div class="container topline">
  
  淡定 执著 进取 自律


</div>


</div>

  <nav class="container nav primary no-print">
  

<a class="homelink" href="https://lvzongcheng.gitee.io">home</a>


  
<a href="https://lvzongcheng.gitee.io/framework" title="Show list of Frameworks">Frameworks</a>

<a href="https://lvzongcheng.gitee.io/java" title="Show list of Java">Java</a>

<a href="https://lvzongcheng.gitee.io/linux" title="Show list of Linux">Linux</a>

<a href="https://lvzongcheng.gitee.io/redis" title="Show list of Redis">Redis</a>

<a href="https://lvzongcheng.gitee.io/sql" title="Show list of SQL">SQL</a>

<a href="https://lvzongcheng.gitee.io/Spring" title="Show list of Spring">Spring</a>

<a href="https://lvzongcheng.gitee.io/tools" title="Show list of Tools">Tools</a>

<a href="https://lvzongcheng.gitee.io/webpage" title="Show list of WebPage">WebPage</a>

<a href="https://lvzongcheng.gitee.io/information" title="Show list of information">information</a>

<a href="https://lvzongcheng.gitee.io/network" title="Show list of network">network</a>

<a href="https://lvzongcheng.gitee.io/%E5%BE%AE%E6%9C%8D%E5%8A%A1" title="Show list of 微服务">微服务</a>

<a href="https://lvzongcheng.gitee.io/%E6%80%BB%E7%BB%93" title="Show list of 总结">总结</a>

<a href="https://lvzongcheng.gitee.io/%E6%9C%BA%E5%99%A8%E8%87%AA%E5%8A%A8%E5%8C%96" title="Show list of 机器自动化">机器自动化</a>

<a href="https://lvzongcheng.gitee.io/%E6%B1%87%E7%BC%96" title="Show list of 汇编">汇编</a>

<a href="https://lvzongcheng.gitee.io/%E8%AE%B0%E4%BA%8B%E6%9C%AC" title="Show list of 记事本">记事本</a>


</nav>

<div class="container nav secondary no-print">
  
<a id="contact-link-email" class="contact_link" rel="me" aria-label="Email" href="mailto:Lyc19880405@163.com">
  <span class="fa fa-envelope-square"></span></a>



<a id="contact-link-github" class="contact_link" rel="me" aria-label="Github" href="https://github.com/enten/hugo-boilerplate">
  <span class="fa fa-github-square"></span></a>




 


















</div>


  

</header>


<article id="main-content" class="container main_content single">
  <header class="container hat">
  <h1>事务
</h1>

</header>

  <div class="container content">
  <h5 id="heading">一、概述：</h5>
<pre><code>  和众多其它数据库一样，Redis作为NoSQL数据库也同样提供了事务机制。
        在Redis中，MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务
        的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生，
        即便如此，我们还是会简要的列出Redis中事务的实现特征：
		
  1). 在事务中的所有命令都将会被串行化的顺序执行，事务执行期间，Redis
        不会再为其它客户端的请求提供任何服务，从而保证了事物中的所有命令被
        原子的执行。
  2). 和关系型数据库中的事务相比，在Redis事务中如果有某一条命令执行失
        败，其后的命令仍然会被继续执行。
  3). 我们可以通过MULTI命令开启一个事务，有关系型数据库开发经验的人可
        以将其理解为&quot;BEGIN TRANSACTION&quot;语句。在该语句之后执行的命令都将
        被视为事务之内的操作，最后我们可以通过执行EXEC/DISCARD命令来提交/回滚
        该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的
        COMMIT/ROLLBACK语句。
  4). 在事务开启之前，如果客户端与服务器之间出现通讯故障并导致网络断开，
        其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生
        在客户端执行EXEC命令之后，那么该事务中的所有命令都会被服务器执行。
  5). 当使用Append-Only模式时，Redis会通过调用系统函数write将该事务内
        的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统
        崩溃，如电源故障导致的宕机，那么此时也许只有部分数据被写入到磁盘，而
        另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的
        一致性检测，一旦发现类似问题，就会立即退出并给出相应的错误提示。此时，
        我们就要充分利用Redis工具包中提供的redis-check-aof工具，该工具可以帮
        助我们定位到数据不一致的错误，并将已经写入的部分数据进行回滚。修复之
        后我们就可以再次重新启动Redis服务器了。
</code></pre>
<h5 id="heading1">二、相关命令列表：</h5>
<p>命令原型	时间复杂度	命令描述	返回值<br>
MULTI	 	用于标记事务的开始，其后执行的命令都将被存入命
令队列，直到执行EXEC时，这些命令才会被原子的执行。	始终
返回OK</p>
<p>EXEC	 	执行在一个事务内命令队列中的所有命令，同时将当前连
接的状态恢复为正常状态，即非事务状态。如果在事务中执行了
WATCH命令，那么只有当WATCH所监控的Keys没有被修改的
前提下，EXEC命令才能执行事务队列中的所有命令，否则EXEC
将放弃当前事务中的所有命令。	原子性的返回事务中各条命令的
返回结果。如果在事务中使用了WATCH，一旦事务被放弃，EXEC
将返回NULL-multi-bulk回复。</p>
<p>DISCARD	 	回滚事务队列中的所有命令，同时再将当前连接的状态
恢复为正常状态，即非事务状态。如果WATCH命令被使用，该命令
将UNWATCH所有的Keys。	始终返回OK。</p>
<p>WATCH key [key &hellip;]	O(1)	在MULTI命令执行之前，可以指定待监
控的Keys，然而在执行EXEC之前，如果被监控的Keys发生修改，
EXEC将放弃执行该事务队列中的所有命令。	始终返回OK。</p>
<p>UNWATCH	O(1)	取消当前事务中指定监控的Keys，如果执行了EXEC
或DISCARD命令，则无需再手工执行该命令了，因为在此之后，事务中
所有被监控的Keys都将自动取消。	始终返回OK。</p>
<h5 id="heading2">三、命令示例：</h5>
<ol>
<li>
<p>事务被正常执行：
#在Shell命令行下执行Redis的客户端工具。
/&gt; redis-cli
#在当前连接上启动一个新的事务。
redis 127.0.0.1:6379&gt; multi
OK
#执行事务中的第一条命令，从该命令的返回结果可以看出，该命令并没有立即执行，而是存于事务的命令队列。
redis 127.0.0.1:6379&gt; incr t1
QUEUED
#又执行一个新的命令，从结果可以看出，该命令也被存于事务的命令队列。
redis 127.0.0.1:6379&gt; incr t2
QUEUED
#执行事务命令队列中的所有命令，从结果可以看出，队列中命令的结果得到返回。
redis 127.0.0.1:6379&gt; exec
1) (integer) 1
2) (integer) 1</p>
</li>
<li>
<p>事务中存在失败的命令：</p>
</li>
</ol>
<pre><code class="language-linux" data-lang="linux">    #开启一个新的事务。
    redis 127.0.0.1:6379&gt; multi
    OK
    #设置键a的值为string类型的3。
    redis 127.0.0.1:6379&gt; set a 3
    QUEUED
    #从键a所关联的值的头部弹出元素，由于该值是字符串类型，而lpop命令仅能用于List类型，因此在执行exec命令时，该命令将会失败。
    redis 127.0.0.1:6379&gt; lpop a
    QUEUED
    #再次设置键a的值为字符串4。
    redis 127.0.0.1:6379&gt; set a 4
    QUEUED
    #获取键a的值，以便确认该值是否被事务中的第二个set命令设置成功。
    redis 127.0.0.1:6379&gt; get a
    QUEUED
    #从结果中可以看出，事务中的第二条命令lpop执行失败，而其后的set和get命令均执行成功，这一点是Redis的事务与关系型数据库中的事务之间最为重要的差别。
    redis 127.0.0.1:6379&gt; exec
</code></pre><pre><code>1) OK  
2) (error) ERR Operation against a key holding the wrong kind of value  
3) OK  
4) &quot;4&quot;  
</code></pre>
<p>2.1 事务中存在语法错误的命令：<br>
3.   回滚事务：</p>
<pre><code>    #为键t2设置一个事务执行前的值。
    redis 127.0.0.1:6379&gt; set t2 tt
    OK
    #开启一个事务。
    redis 127.0.0.1:6379&gt; multi
    OK
    #在事务内为该键设置一个新值。
    redis 127.0.0.1:6379&gt; set t2 ttnew
    QUEUED
    #放弃事务。
    redis 127.0.0.1:6379&gt; discard
    OK
    #查看键t2的值，从结果中可以看出该键的值仍为事务开始之前的值。
    redis 127.0.0.1:6379&gt; get t2
    &quot;tt&quot;
</code></pre><h5 id="watchcas">四、WATCH命令和基于CAS的乐观锁：</h5>
<pre><code>  在Redis的事务中，WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys，倘若在WATCH之后有任何Key的值发生了变化，EXEC命令执行的事务都将被放弃，同时返回Null multi-bulk应答以通知调用者事务执行失败。例如，我们再次假设Redis中并未提供incr命令来完成键值的原子性递增，如果要实现该功能，我们只能自行编写相应的代码。其伪码如下：
  val = GET mykey
  val = val + 1
  SET mykey $val
  以上代码只有在单连接的情况下才可以保证执行结果是正确的，因为如果在同一时刻有多个客户端在同时执行该段代码，那么就会出现多线程程序中经常出现的一种错误场景--竞态争用(race condition)。比如，客户端A和B都在同一时刻读取了mykey的原有值，假设该值为10，此后两个客户端又均将该值加一后set回Redis服务器，这样就会导致mykey的结果为11，而不是我们认为的12。为了解决类似的问题，我们需要借助WATCH命令的帮助，见如下代码：
  WATCH mykey
  val = GET mykey
  val = val + 1
  MULTI
  SET mykey $val
  EXEC
  和此前代码不同的是，新代码在获取mykey的值之前先通过WATCH命令监控了该键，此后又将set命令包围在事务中，这样就可以有效的保证每个连接在执行EXEC之前，如果当前连接获取的mykey的值被其它连接的客户端修改，那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。
</code></pre>

</div>


  
</article>
      <footer id="main-footer" class="container main_footer">
  

  <div class="container nav foot no-print">
  
<a href="https://lvzongcheng.gitee.io/license">license</a>


  <a class="toplink" href="#">back to top</a>

</div>

  <div class="container credits">
  
<div class="container footline">
  
  code with <!-- raw HTML omitted --><!-- raw HTML omitted -->


</div>


  
<div class="container copyright">
  
  (c) 2015 yourname.


</div>


</div>

</footer>

    </main>
    
<script type="text/javascript">
  (function() {
    
    
    if (window.location.hostname == "localhost")
      return;
    var dsq = document.createElement('script'); dsq.async = true; dsq.type = 'text/javascript';
    dsq.src = '//your_disqus_shortname.disqus.com/count.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
  })();
</script>



<script src="/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>



    
  </body>
</html>

